package com.hrt.db;

import java.util.List;

import com.btcode.db.executer.IDataExecuter;
import com.btcode.db.sqlparameter.SQLParameterList;
import com.btcode.exception.BusinessException;
import com.btcode.exception.MsgException;
import com.btcode.orm.cache.IORMCacheExecuter;
import com.btcode.orm.conver.IModelConver;
import com.btcode.orm.conver.IValueConver;
import com.btcode.orm.exception.PrimarykeyEmptyException;
import com.btcode.orm.exception.PrimarykeyMissException;
import com.btcode.orm.executer.DefaultORMExecuter;
import com.btcode.orm.model.FieldValue;
import com.btcode.orm.model.IField;
import com.btcode.orm.model.IModel;
import com.btcode.orm.model.IModelAddition;
import com.btcode.orm.relation.ex.IRelation;
import com.btcode.common.StringUtil;

public class ORMExecuterForSQLLit extends DefaultORMExecuter {

    public ORMExecuterForSQLLit(IDataExecuter dataExecuter, IModelConver modelConver, IORMCacheExecuter ormCache,
            IValueConver valueConver) {
        super(dataExecuter, modelConver, ormCache, valueConver);
    }

    @Override
    public <T extends IModel> void creates(List<T> models) {

        if (models == null || models.size() <= 0) {
            return;
        }

        SQLParameterList sqlParam = new SQLParameterList();

        for (int i = 0; i < models.size(); i++) {

            T model = models.get(i);

            SQLParameterList param = buildCreateParameter(model, i);
            sqlParam.putAll(param);
        }
        /**
         * 这一句的执行顺序必须放在最后，因为上面的model在获取参数的时候，会变化
         */
        String sql = buildCreateSQL(models.get(0), models.size());

        dataExecuter.create(sql, sqlParam);
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    private <T extends IModel> SQLParameterList buildCreateParameter(T model, Integer index) {

        if (model == null) {
            return null;
        }

        IModelAddition modelAddition = model.getModelAddition();

        if (modelAddition != null) {
            modelAddition.generateDefaultValue(model);
        }

        checkModelPrimarykeyIsMissOrEmpty(model);

        if (modelAddition != null) {
            modelAddition.beforeCreate(model);
        }

        SQLParameterList parameters = model.toSQLParameter(index);

        return parameters;
    }

    private <T extends IModel> String buildCreateSQL(T model, int size) {

        if (model == null) {
            return null;
        }

        List<IField> fields = model.getFields();

        StringBuffer sqlBuffer = new StringBuffer();
        sqlBuffer.append("INSERT INTO ");
        sqlBuffer.append(model.getTableName());
        sqlBuffer.append(" ( ");

        StringBuffer valueBuffer = new StringBuffer();

        for (int i = 0; i < fields.size(); i++) {

            IField field = fields.get(i);

            Object value = model.getFieldValue(field.getFieldName());
            value = valueConver.conveToSQLValue(field, value);

            if (value == null) {
                continue;
            }
            sqlBuffer.append(field.getFieldName());
            sqlBuffer.append(",");
        }

        for (int i = 0; i < size; i++) {

            valueBuffer.append("\r\n select ");

            for (int j = 0; j < fields.size(); j++) {

                IField field = fields.get(j);

                Object value = model.getFieldValue(field.getFieldName());
                value = valueConver.conveToSQLValue(field, value);

                if (value == null) {
                    continue;
                }
                valueBuffer.append("@" + field.getFieldName() + i);
                valueBuffer.append(",");
            }

            valueBuffer.deleteCharAt(valueBuffer.lastIndexOf(","));
            valueBuffer.append("\r\n union all");
        }

        sqlBuffer.deleteCharAt(sqlBuffer.lastIndexOf(","));
        sqlBuffer.append(" ) ");
        
        String sql = sqlBuffer.toString() + valueBuffer.toString();
        
        sql = StringUtil.replaceLast(sql, "union all", "");

        System.out.println(sql);

        return sql;

    }

    @Override
    public <T extends IModel> void create(T model) {
        createPureModel(model);
        updateRelationModels(model);
    }

    @Override
    public <T extends IModel> void update(T model) {
        try {
            updateRelationModels(model);
            updatePureModel(model);
        } 
        catch (Exception e) {
            if (e instanceof BusinessException) {
                throw e;
            }
            throw new MsgException("更新出错", e);
        }
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    private <T extends IModel> void createPureModel(T model) {

        if (model == null) {
            return;
        }

        IModelAddition modelAddition = model.getModelAddition();

        if (modelAddition != null) {
            modelAddition.beforeCreate(model);
        }

        SQLParameterList sqlParameters = buildCreateParameter(model);
        String createSQL = buildCreateSQL(model);

        dataExecuter.create(createSQL, sqlParameters);

        ormCache.deleteModelCache(model);

        if (modelAddition != null) {
            modelAddition.afterCreate(model);
        }

    }

    private <T extends IModel> void updateRelationModels(T model) {

        List<IRelation> relations = model.getRelations();

        /**
         * 再获取一次model，因为有些情况下，关联表所需要的外键值并没有传进来
         */
        T dataSourceModel = getModelWithOutRelation(model);

        for (IRelation relation : relations) {

            /**
             * 主表外键字段
             */
            IField masterField = relation.getMasterField();
            /**
             * 从表关联字段
             */
            IField slaveField = relation.getSlaveField();

            List<IModel> slaveModels = model.getRelationModel(slaveField.getTableName());

            /**
             * 如果关联数据为空，则不做处理
             */
            if (slaveModels == null) {
                continue;
            }

            /**
             * 主表外键字段的值
             */
            Object keyValue = dataSourceModel.getFieldValue(masterField.getFieldName());

            /**
             * 重新设置下外键的值，确保可以关联上，避免业务层忘记这一步
             */
            for (IModel slaveModel : slaveModels) {
                slaveModel.setFieldValue(slaveField.getFieldName(), keyValue);
            }

            /**
             * 删除关联关系
             */
            if (relation.isPrimaryRelationCache()) {
                ormCache.deleteRelationCacheModels(model, relation);
            }

            /**
             * 如果是多对多关联就简单处理，先删除后插入
             */
            if (relation.isManyToMany()) {
                String deleteSQL = " DELETE FROM " + slaveField.getTableName() + " WHERE " + slaveField.getFieldName()
                        + "=@" + slaveField.getFieldName();
                SQLParameterList pc = new SQLParameterList();
                pc.put(slaveField.getFieldName(), keyValue);
                dataExecuter.delete(deleteSQL, pc);

                creates(slaveModels);
            }
            else {
                /**
                 * 如果是一对多，需要谨慎处理，因为从表可能会被其他业务所使用，不能简单先删除后插入.
                 * 先把从表中相关记录的外键置空，使其脱离关联，再把新的关联更新入库。
                 */
                String updateSlaveSQL = " UPDATE " + slaveField.getTableName() + " SET " + slaveField.getFieldName()
                        + " = null WHERE " + slaveField.getFieldName() + "=@" + slaveField.getFieldName();
                SQLParameterList pc = new SQLParameterList();
                pc.put(slaveField.getFieldName(), keyValue);
                dataExecuter.update(updateSlaveSQL, pc);

                for (IModel slaveModel : slaveModels) {
                    /**
                     * 删除从表缓存
                     */
                    if (slaveModel.isNeedCache()) {
                        ormCache.deleteModelCache(slaveModel);
                    }

                    try {
                        checkModelPrimarykeyIsMissOrEmpty(slaveModel);
                    }
                    catch (PrimarykeyMissException e) {
                        throw e;
                    } 
                    catch (PrimarykeyEmptyException e) {
                        /**
                         * 如果主键为null，认为是要新增
                         */
                        create(slaveModel);
                        continue;
                    }

                    if (getModel(slaveModel) == null) {
                        /**
                         * 如果库里不存在，认为是要新增
                         */
                        create(slaveModel);
                    } 
                    else {
                        /**
                         * 如果既有主键值，在库里又存在，那么就更新
                         */
                        update(slaveModel);
                    }
                }
            }
        }

    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    private <T extends IModel> void updatePureModel(T model) {

        if (model == null) {
            return;
        }

        /**
         * 主键不能为空
         */
        checkModelPrimarykeyIsMissOrEmpty(model);

        IModelAddition modelAddition = model.getModelAddition();

        if (modelAddition != null) {
            modelAddition.beforeUpdate(model);
        }

        List<IField> keyField = model.getKeyFields();

        List<IField> fields = model.getFields();

        StringBuffer whereStrBuffer = new StringBuffer(" WHERE ");
        StringBuffer updateStrBuffer = new StringBuffer(" UPDATE " + model.getTableName() + " SET ");
        SQLParameterList parameters = new SQLParameterList();

        for (int i = 0; i < keyField.size(); i++) {

            IField field = keyField.get(i);
            Object value = model.getFieldValue(field.getFieldName());

            whereStrBuffer.append(field.getFieldName() + " = @" + field.getFieldName());
            parameters.put(field.getFieldName(), value);

            if (i < keyField.size() - 1) {
                whereStrBuffer.append(" AND ");
            }
        }

        for (IField field : fields) {

            if (!field.isForUpdate()) {
                continue;
            }

            Object value = model.getFieldValue(field.getFieldName());

            if (value == null) {
                continue;
            }
            /**
             * 如果是nullValue，则使用置空处理
             */
            if (value.equals(FieldValue.nullValue)) {
                updateStrBuffer.append(field.getFieldName() + "=null,");
            }
            else {
                updateStrBuffer.append(field.getFieldName() + "=@" + field.getFieldName() + ",");
                parameters.put(field.getFieldName(), value);
            }
        }
        updateStrBuffer.replace(updateStrBuffer.length() - 1, updateStrBuffer.length(), " ");

        dataExecuter.update(updateStrBuffer.toString() + whereStrBuffer.toString(), parameters);

        if (model.isNeedCache()) {
            ormCache.deleteModelCache(model);
        }

        if (modelAddition != null) {
            modelAddition.afterUpdate(model);
        }
    }

}
